/*
 * Copyright (c) 2021 The red-star Project
 *
 * Licensed under the Apache License, version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.inyourcode.core.serialization.protobuf;

import com.google.protobuf.AbstractMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Parser;
import com.inyourcode.core.util.StackTraceUtil;
import com.inyourcode.core.serialization.api.Serializer;
import com.inyourcode.core.serialization.api.SerializerType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author JackLei
 */
public class ProtobufSerializer extends Serializer {
    private static final Logger logger = LoggerFactory.getLogger(ProtobufSerializer.class);

    public ProtobufSerializer() {
    }

    public byte code() {
        return SerializerType.PROTOBUF.value();
    }

    @Override
    public <T> byte[] writeObject(T obj) {
        return ((AbstractMessage.Builder)obj).build().toByteArray();
    }

    @Override
    public <T> T readObject(byte[] bytes, int offset, int length, Class<T> messageClazz) {
        Method method = null;
        try {
            method = messageClazz.getDeclaredMethod("parser");

            if (method == null) {
                logger.error("The parser field was not found in {} ", messageClazz);
                return null;
            }

            method.setAccessible(true);
            Object parser = method.invoke(messageClazz);
            if (parser == null) {
                logger.error("The parser obj is null in {}", messageClazz);
                return null;
            }

            if (!(parser instanceof Parser)) {
                logger.error("This parser obj type does not match,parser:{},clazz:{}", parser, messageClazz);
                return null;
            }

            return (T) ((Parser) parser).parseFrom(bytes);
        } catch (NoSuchMethodException e) {
            logger.error(" Protobuf serialization failed0, clazz:{}, exception:{}",  messageClazz, StackTraceUtil.stackTrace(e));
        } catch (IllegalAccessException e) {
            logger.error(" Protobuf serialization failed1, clazz:{}, exception:{}",  messageClazz, StackTraceUtil.stackTrace(e));
        } catch (InvocationTargetException e) {
            logger.error(" Protobuf serialization failed2, clazz:{}, exception:{}",  messageClazz, StackTraceUtil.stackTrace(e));
        } catch (InvalidProtocolBufferException e) {
            logger.error(" Protobuf serialization failed3, clazz:{}, exception:{}",  messageClazz, StackTraceUtil.stackTrace(e));
        }
        return null;
    }

    @Override
    public String toString() {
        return "proto_buff:(code=" + code() + ")";
    }

}
